﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Reflection.Metadata;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Timers;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Logging;
using mqtt;
using Newtonsoft.Json;
using NPOI.OpenXmlFormats;
using NPOI.OpenXmlFormats.Spreadsheet;
using NPOI.POIFS.Properties;
using NPOI.Util;
using NPOI.XWPF.UserModel;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric;
using Pipelines.Sockets.Unofficial.Arenas;
using Simple.Common.Configuration;
using Simple.Common.Middlewares;
using Simple.Repository.Enum;
using Simple.Repository.Models;
using Simple.Repository.Models.Attendance;
using Simple.Repository.Models.DeviceManagement;
using Simple.Repository.Models.Market;
using Simple.Repository.Models.MorningCheck;
using Simple.Repository.Models.SyncHistory;
using Simple.Services.MQTT.Model;
using Simple.Services.System.FileInfo;
using Simple.Services.System.Market.Models;
using Simple.Services.System.MorningCheck.Models;
using SixLabors.ImageSharp;
using StackExchange.Redis;
using UAParser;
using ZXing;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
using Device = Simple.Repository.Models.DeviceManagement.Device;
using Parent = Simple.Repository.Models.Parent;
using Timer = System.Timers.Timer;



namespace Simple.Services.System.MorningCheck
{
    public class ClockingInMorningCheckService : BusinessBaseService
    {
        private readonly SimpleDbContext _context;
        private readonly ILogger _logger;
        private readonly FileInfoService _fileInfoService;
        private readonly CacheService _cacheService;
        private Timer _timer;

        public ClockingInMorningCheckService(SimpleDbContext context,
            ISimpleService services,
            ILogger<ApiExceptionMiddleware> logger,
            FileInfoService fileInfoService,
            CacheService cacheService) : base(services)
        {
            _context = context;
            _logger = logger;
            _fileInfoService = fileInfoService;
            _cacheService = cacheService;
        }

        private async Task<string> MqttSyncData(string topic, List<Info> info)
        {
            var data = new MqttPersOutModel();
            data.PersonNum = info.Count;
            data.Operator = "EditPersonsNew";
            data.messageId = DateTime.Now.Ticks.ToString();
            data.info = info;

            var dataStr = JsonConvert.SerializeObject(data);
            dataStr = dataStr.Replace("Operator", "operator");
            MqttService.PublishData(dataStr, topic, "702d2681-e596-43b9-9e51-96716caed111");
            return data.messageId;
        }

        private async Task DleMqttSyncData(string topic)
        {
            var data = new DleMQTTModel();
            data.Operator = "DeleteAllPerson";
            data.messageId = DateTime.Now.Ticks.ToString();
            data.info = new List<DleMQTTInfoModel> { new DleMQTTInfoModel() { deleteall = "1" } };

            var dataStr = JsonConvert.SerializeObject(data);
            dataStr = dataStr.Replace("Operator", "operator");
            MqttService.PublishData(dataStr, topic, "702d2681-e596-43b9-9e51-96716caed111");
        }


        /// <summary>
        /// 小程序单个同步数据到考勤机
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public async Task<string> SingleSyncDataAsync(SingleSyncDataQueryModel query)
        {

            var deviceList = await GetDeviceList(new List<Guid>());
            deviceList = deviceList.Where(x => x.OrganizationId == _currentUser.OrganizationId && x.DeviceType == (int)EnumDeviceType.ClockingIn).ToList();

            var infoList = new List<Info>();
            var In = new Info();
            In.personType = 0;
            In.tempCardType = 0;
            var imgId = string.Empty;

            var typeName = string.Empty;
            var userId = string.Empty;

            switch (query.SyncDataType)
            {
                case EnumSyncDataType.Childrens:
                    var child = await _context.Set<Childrens>().Where(p => p.Id == query.UserId).FirstAsync();

                    userId = child.StudentNumber;
                    In.name = child.Name;
                    In.gender = child.Gender == GenderType.Male ? 0 : 1;
                    In.telnum1 = ((int)query.SyncDataType).ToString();   //设备没有字段区分学生还是老师，用这个字段代替
                    imgId = child.ImgId.ToString();

                    typeName = "学生";
                    break;
                case EnumSyncDataType.Teacher:
                    var teacherData = await _context.Set<SysUser>().Where(x => x.Id == query.UserId).FirstOrDefaultAsync();

                    userId = teacherData.UserName.ToString();
                    In.name = teacherData.Name;
                    In.gender = teacherData.Gender == GenderType.Male ? 0 : 1;
                    In.telnum1 = ((int)query.SyncDataType).ToString();   //设备没有字段区分学生还是老师，用这个字段代替
                    imgId = await _fileInfoService.GetUserPortrait(teacherData.Id);


                    typeName = "老师";
                    break;
                case EnumSyncDataType.Parent:
                    var parent = await _context.Set<Parent>().Where(x => x.Id == query.UserId).FirstOrDefaultAsync();

                    userId = parent.PhoneNumber.ToString();
                    In.name = parent.Name;
                    In.gender = parent.GenderType == GenderType.Male ? 0 : 1;
                    In.telnum1 = ((int)query.SyncDataType).ToString();   //设备没有字段区分学生还是老师，用这个字段代替
                    if (!string.IsNullOrEmpty(parent.MachineCardNumber))
                    {
                        In.RFIDCard = Int64.Parse(parent.MachineCardNumber);
                        In.cardType2 = 2;
                        In.cardNum2 = Int64.Parse(parent.MachineCardNumber);
                    }
                    imgId = parent.ImgId.ToString();

                    typeName = "家长";
                    break;
                default:
                    break;
            }
            if (string.IsNullOrEmpty(imgId))
            {
                imgId = "08dbe0e9-6516-402c-83f0-404808cc16d5";
            }
            In.customId = userId;
            In.idCard = userId;
            In.picURI = $"https://tianleletao.com:6001/api/sysFileInfo/previewImg?id={imgId}";

            _logger.LogError($"单个同步数据：同步类型：{typeName},ID：{query.UserId}，姓名：{In.name}");

            infoList.Add(In);
            var result = string.Empty;

            foreach (var item in deviceList)
            {
                var isApplicablePopulation = item.ApplicablePopulation.IndexOf(((int)query.SyncDataType).ToString()) > -1;
                if (!string.IsNullOrEmpty(item.ApplicablePopulation) && isApplicablePopulation)
                {
                    if (infoList.Count > 0)
                    {
                        result = await MqttSyncData(item.IpAddress, infoList);
                    }
                }
            }

            if (query.SyncDataType == EnumSyncDataType.Childrens)
            {
                await SyncChildrensMorningCheck(new BatchSyncChildrensMorningCheckQueryModel()
                {
                    DeviceIds = null,
                    OrganizationId = _currentUser.OrganizationId,
                    UserIds = new List<Guid>() { query.UserId }
                }, result);
            }

            return result;
        }

        /// <summary>
        /// 批量同步数据到考勤机器
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task SyncDataAsync(BatchSyncDataQueryModel model)
        {
            var deviceList = await GetDeviceList(model.DeviceIds);

            deviceList = deviceList.Where(x => x.OrganizationId == model.OrganizationId).ToList();
            switch (model.SyncDataType)
            {
                // 1：学生 
                case EnumSyncDataType.Childrens:
                    var childrensQuery = _context.Set<Childrens>().Where(x => x.ChildState == EnumChildState.Reading && x.OrganizationId == model.OrganizationId).AsQueryable();
                    if (model.UserIds != null && model.UserIds.Count > 0)
                    {
                        childrensQuery = childrensQuery.Where(x => model.UserIds.Contains(x.Id));
                    }
                    var childrensList = await childrensQuery.ToListAsync();

                    if (deviceList.Where(x => x.DeviceType == (int)EnumDeviceType.MorningCheck).Count() > 0)
                    {
                        await SyncChildrensMorningCheck(new BatchSyncChildrensMorningCheckQueryModel()
                        {
                            DeviceIds = null,
                            OrganizationId = _currentUser.OrganizationId,
                            UserIds = model.UserIds
                        });
                    } 
                    await Sync(deviceList, childrensList, (int)model.SyncDataType);
                    break;
                case EnumSyncDataType.Teacher: // 2：老师
                    var teacherQuery = _context.Set<SysUser>().Where(x => x.OrganizationId == model.OrganizationId).AsQueryable();
                    if (model.UserIds != null && model.UserIds.Count > 0)
                    {
                        teacherQuery = teacherQuery.Where(x => model.UserIds.Contains(x.Id));
                    }
                    var teacherList = await teacherQuery.ToListAsync();
                    await Sync(deviceList, teacherList, (int)model.SyncDataType);
                    break;
                case EnumSyncDataType.Parent:  //3：家长
                    var parentQuery = _context.Set<Parent>().Where(x => x.OrganizationId == model.OrganizationId).AsQueryable();
                    if (model.UserIds != null && model.UserIds.Count > 0)
                    {
                        parentQuery = parentQuery.Where(x => model.UserIds.Contains(x.Id));
                    }
                    var parentList = await parentQuery.ToListAsync();
                    await Sync(deviceList, parentList, (int)model.SyncDataType);
                    break;
            }
        }

        private async Task Sync<T>(List<Device> deviceList, List<T> userList, int syncDataType)
        {
            foreach (var item in deviceList)
            {
                if (item.DeviceType == (int)EnumDeviceType.ClockingIn)
                {
                    var isApplicablePopulation = item.ApplicablePopulation.IndexOf(syncDataType.ToString()) > -1;
                    if (!string.IsNullOrEmpty(item.ApplicablePopulation) && isApplicablePopulation)
                    {
                        var info = new List<Info>();
                        foreach (var user in userList)
                        {
                            var In = new Info();
                            var userId = "";
                            var userName = "";
                            var gender = 0;
                            var imgId = "";
                            if (user is SysUser)
                            {
                                var d = user as SysUser;
                                userId = d.UserName;
                                userName = d.Name;
                                gender = d.Gender == GenderType.Male ? 0 : 1;
                                In.telnum1 = ((int)EnumSyncDataType.Teacher).ToString();   //设备没有字段区分学生还是老师，用这个字段代替
                                imgId = await _fileInfoService.GetUserPortrait(d.Id);


                            }
                            else if (user is Childrens)
                            {
                                var d = user as Childrens;
                                userId = d.StudentNumber;
                                userName = d.Name;
                                gender = d.Gender == GenderType.Male ? 0 : 1;
                                imgId = d.ImgId.ToString();
                                In.telnum1 = ((int)EnumSyncDataType.Childrens).ToString();  //设备没有字段区分学生还是老师，用这个字段代替
                            }
                            if (user is Parent)
                            {
                                var d = user as Parent;
                                userId = d.PhoneNumber;
                                userName = d.Name;
                                gender = d.GenderType == GenderType.Male ? 0 : 1;
                                imgId = d.ImgId.ToString();
                                if (!string.IsNullOrEmpty(d.MachineCardNumber))
                                {
                                    In.RFIDCard = Int64.Parse(d.MachineCardNumber);
                                    In.cardType2 = 2;
                                    In.cardNum2 = Int64.Parse(d.MachineCardNumber);
                                }
                                In.telnum1 = ((int)EnumSyncDataType.Parent).ToString();  //设备没有字段区分学生还是老师，用这个字段代替

                            }
                            In.customId = userId;
                            In.idCard = userId;
                            In.name = userName;
                            In.personType = 0;
                            In.tempCardType = 0;
                            In.gender = gender;
                            if (string.IsNullOrEmpty(imgId))
                            {
                                imgId = "08dbe0e9-6516-402c-83f0-404808cc16d5";
                            }
                            In.picURI = $"https://tianleletao.com:6001/api/sysFileInfo/previewImg?id={imgId}";

                            if (item.IsFace.HasValue && !item.IsFace.Value)
                            {
                                In.picURI = $"https://tianleletao.com:6001/api/sysFileInfo/previewImg?id=08dbe0e9-6516-402c-83f0-404808cc16d5";
                            }
                            if (item.IsCard.HasValue && !item.IsCard.Value)
                            {
                                In.RFIDCard = 0;
                                In.cardType2 = 0;
                                In.cardNum2 = 0;
                            }
                            info.Add(In);
                        }
                        _logger.LogError($"批量同步");
                        await MqttSyncData(item.IpAddress, info);
                    }
                }
            }
        }

        private async Task<List<Device>> GetDeviceList(List<Guid> DeviceIds)
        {
            var qeruy = _context.Set<Device>().AsQueryable();
            if (DeviceIds.Count > 0)
            {
                qeruy = qeruy.Where(x => DeviceIds.Contains(x.Id));
            }
            var deviceList = await qeruy.ToListAsync();
            return deviceList;
        }

        /// <summary>
        /// 考勤回调
        /// </summary>
        /// <param name="dataStr"></param>
        /// <returns></returns>
        public async Task AttendanceCallback(string dataStr)
        {
            //dataStr = "{\r\n\"operator\": \"RecPush\",\r\n\"info\": {\r\n\"customId\":\"15387568779\",\r\n\"personId\":\"1441\",\r\n\"RecordID\":\"524\",\r\n\"VerifyStatus\":\"1\",\r\n\"PersonType\":\"0\",\r\n\"similarity1\":\"100.000000\",\r\n\"similarity2\":\"0.000000\",\r\n\"Sendintime\":0,\r\n\"direction\":\"entr\",\r\n\"otype\":\"24\",\r\n\"persionName\":\"曹爱香\",\r\n\"facesluiceId\":\"2354641\",\r\n\"facesluiceName\":\"家长进左\",\r\n\"idCard\":\"15387568779\",\r\n\"telnum\":\"3\",\r\n\"left\":\"29\",\r\n\"top\":\"125\",\r\n\"right\":\"267\",\r\n\"bottom\":\"355\",\r\n\"time\":\"2023-12-21 12:09:33\",\r\n\"PushType\": \"0\",\r\n\"OpendoorWay\": \"0\",\r\n\"cardNum2\": \"2321232181\",\r\n\"szQrCodeData\":\"\",\r\n\"isNoMask\": \"0\"\r\n}\r\n}";
            var recPushResult = JsonConvert.DeserializeObject<RecPushResult>(dataStr);

            var data = new AttendanceRecord();

            data.AttendanceTime = recPushResult.info.time;


            if (!string.IsNullOrEmpty(recPushResult.info.customId) && recPushResult.info.customId != " ")
            {
                data.UserNumber = recPushResult.info.customId;
            }
            else if (!string.IsNullOrEmpty(recPushResult.info.idCard) && recPushResult.info.idCard != " ")
            {
                data.UserNumber = recPushResult.info.idCard;
            }
            else
            {
                data.UserNumber = string.Empty;
            }

            data.DeviceCode = recPushResult.info.facesluiceId;
            data.OpenDoorType = string.IsNullOrEmpty(recPushResult.info.pic) ? 1 : 2;
            data.EntranceType = recPushResult.info.direction == "entr" ? 1 : 2;
            data.UserName = recPushResult.info.persionName;

            switch (int.Parse(recPushResult.info.telnum)) //设备没有字段区分学生还是老师，用这个字段代替
            {

                case (int)EnumSyncDataType.Childrens: // 1：学生 
                    data.AttendanceType = 2;
                    break;
                case (int)EnumSyncDataType.Teacher:  // 2：老师
                    data.AttendanceType = 1;
                    break;
                case (int)EnumSyncDataType.Parent:  //3：家长
                    data.AttendanceType = 2;
                    var parentData = await _context.Set<Parent>().Where(x => x.PhoneNumber == recPushResult.info.customId).FirstAsync();
                    var childData = await _context.Set<ChildrenParent>().Include(x => x.Child).Where(x => x.ParentId == parentData.Id).FirstAsync();
                    data.UserNumber = childData.Child.StudentNumber;
                    break;
            }

            try
            {
                if (data.EntranceType == 1 && data.AttendanceType == 2 && !string.IsNullOrEmpty(data.UserNumber)) //调用喇叭
                {
                    await AudioBroadcasting(data.UserNumber);
                }
            }
            catch (Exception)
            {
            }
            await _context.AddAsync(data);
            int ret = await _context.SaveChangesAsync();
        }

        /// <summary>
        /// 语音播报到校
        /// </summary>
        /// <param name="studentNumber"></param>
        /// <returns></returns>
        public async Task AudioBroadcasting(string studentNumber)
        {
            _logger.LogError($"AudioBroadcasting");
            // 全园所有学生
            var childrens = await _context.Set<Childrens>().Where(x => x.ChildState == EnumChildState.Reading && x.StudentNumber == studentNumber).FirstOrDefaultAsync();
            if (childrens == null)
            {
                _logger.LogError($"AudioBroadcasting，找不到学生,学生学号:{studentNumber}");
                return;
            }

            //所有音响设备
            var c_device = await _context.Set<Device>().Where(x => x.DeviceType == (int)EnumDeviceType.sound && x.ClassesId.ToString() == childrens.ClassesId).FirstOrDefaultAsync();
            if (c_device == null)
            {
                _logger.LogError($"AudioBroadcasting，找不到班级对应的音箱,班级ID:{childrens.ClassesId}");
                return;
            }

            string voicetemp = AppSettings.DeviceConfig.VoiceTemp;
            string posturl = AppSettings.DeviceConfig.PostUrl;
            string loginPostUrl = AppSettings.DeviceConfig.LoginPostUrl;

            ////推送到音响
            if (childrens != null && c_device != null)
            {

                string _token = string.Empty;

                var loginUrl = string.Format(loginPostUrl, c_device.IpAddress);
                var resultJson = RequestHelper.DevicePost(loginUrl, "{\"username\":\"admin\",\"password\":\"bobo258369\"}");
                var faceJsonObject = JsonConvert.DeserializeObject<AudioBroadcastingDto>(resultJson);
                if (faceJsonObject != null && faceJsonObject.code == 200)
                {
                    _token = faceJsonObject.value.token;
                }

                posturl = string.Format(posturl, c_device.IpAddress);
                voicetemp = string.Format(voicetemp, childrens.Name);
                DevicePlayModelcs devicePlayModelcs = new DevicePlayModelcs { text = voicetemp, device_codes = new string[] { c_device.Number }, volume = c_device.Volume.Value };
                var ps = JsonConvert.SerializeObject(devicePlayModelcs);
                var result = RequestHelper.DevicePost(posturl, ps, _token);

                _logger.LogError($"result:{result}");

            }
        }

        public async Task DeleteAllUser(BatchSyncDataQueryModel model)
        {
            var deviceList = await GetDeviceList(model.DeviceIds);
            foreach (var item in deviceList)
            {
                await DleMqttSyncData(item.IpAddress);
            }
        }

        /// <summary>
        /// 同步数据到晨检机
        /// </summary>
        /// <param name="batchSyncChildrensMorningCheckQueryModel"></param>
        /// <returns></returns>
        public async Task SyncChildrensMorningCheck(BatchSyncChildrensMorningCheckQueryModel queryModel, string messageId = null)
        {
            var childrens = await _context.Set<Childrens>()
                  .Where(x => x.ChildState == EnumChildState.Reading).ToListAsync();
            childrens = childrens.Where(x => x.OrganizationId == queryModel.OrganizationId).ToList();
            if (queryModel.UserIds != null && queryModel.UserIds.Count > 0)
            {
                childrens = childrens.Where(x => queryModel.UserIds.Contains(x.Id)).ToList();
            }


            var deviceList = await GetDeviceList(new List<Guid>());
            deviceList = deviceList.Where(x => x.OrganizationId == queryModel.OrganizationId && x.DeviceType == (int)EnumDeviceType.MorningCheck).ToList();
            if (queryModel.DeviceIds != null && queryModel.DeviceIds.Count > 0)
            {
                deviceList = deviceList.Where(x => queryModel.DeviceIds.Contains(x.Id)).ToList();
            }

            var childrenList = new List<ChildrenModel>();
            if (deviceList.Count > 0)
            {
                var imgids = childrens.Select(x => x.ImgId).ToList();
                var files = await _context.Set<SysFileInfo>().Where(x => imgids.Contains(x.Id)).ToListAsync();
                foreach (var item in childrens)
                {
                    var base64str = await _fileInfoService.GetBase64Str(files.FirstOrDefault(x => x.Id == item.ImgId));
                    if (!string.IsNullOrEmpty(base64str))
                    {
                        var chimodel = new ChildrenModel()
                        {
                            StudentNumber = item.StudentNumber,
                            Name = item.Name,
                            Base64str = base64str
                        };
                        childrenList.Add(chimodel);
                    }

                }
            }
            foreach (var device in deviceList)
            {
                _logger.LogError($"同步到晨检机器 Count：{childrenList.Count}");

                foreach (var children in childrenList)
                {
                    var mqdata = new MorningCheckDto();
                    if (!string.IsNullOrEmpty(messageId))
                    {
                        mqdata.from = messageId;
                    }
                    else
                    {
                        mqdata.from = DateTime.Now.Ticks.ToString();
                    }
                    mqdata.data = new MorningCheckDataDto()
                    {
                        cmd = "addUser",
                        user_id = children.StudentNumber,
                        name = children.Name,
                        id_valid = "",
                        face_template = HttpUtility.UrlEncode(children.Base64str)
                    };

                    var dataStr = JsonConvert.SerializeObject(mqdata);
                    MqttService.PublishData(dataStr, device.IpAddress, "702d2681-e596-43b9-9e51-96716caed111");

                }
            }

        }

        /// <summary>
        /// 同步考勤数据回调
        /// </summary>
        /// <param name="dataStr"></param>
        /// <returns></returns>
        public async Task SyncCallBack(string dataStr, int type)
        {
            var syncHistory = new SyncHistory();
            if (type == 1)
            {
                var result = JsonConvert.DeserializeObject<SyncDataCallBackDto>(dataStr);
                syncHistory.MessageId = result.messageId;
                syncHistory.AddErrNum = result.info.AddErrNum;
                syncHistory.AddSucNum = result.info.AddSucNum;
                syncHistory.AddErrInfo = JsonConvert.SerializeObject(result.info.AddErrInfo);
                syncHistory.SyncTime = DateTime.Now;
            }
            else
            {
                var result = JsonConvert.DeserializeObject<MorningCheckSyncResultDto>(dataStr);
                if (result.data.code != 0)
                {
                    syncHistory.MessageId = result.to;
                    syncHistory.AddErrInfo = result.data.msg;
                }

            }

            await _context.AddAsync(syncHistory);
            await _context.SaveChangesAsync();

        }

        /// <summary>
        /// 查询同步结果
        /// </summary>
        /// <param name="dataStr"></param>
        /// <returns></returns>
        public async Task<SyncResult> GetSyncCallBack(string messageId)
        {
            var result = new SyncResult()
            {
                ClockingInMessage = "考勤同步成功",
                MorningCheckMessage = "晨检同步成功"
            };
            var message = await _context.Set<SyncHistory>().Where(x => x.MessageId == messageId).FirstOrDefaultAsync();
            if (message != null)
            {
                if (message.Type == 1 && !string.IsNullOrEmpty(message.AddErrInfo))
                {
                    var errInfo = JsonConvert.DeserializeObject<List<ErrInfo>>(message.AddErrInfo);
                    switch (errInfo[0].errcode)
                    {
                        case "465":
                            result.ClockingInMessage = "考勤同步失败,获取图片超时或者下载图片失败,请重试或更换图片";
                            break;
                        case "468":
                            result.ClockingInMessage = "考勤同步失败，提取图片人脸特征失败,请更换图片";
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(message.AddErrInfo))
                    {
                        result.MorningCheckMessage = message.AddErrInfo;
                    }
                }
            }
            return result;

        }


    }
}
